/*-----------------------------------------------------------------------------------*/
/* Nuvoton Electronics Corporation confidential                                      */
/*                                                                                   */
/* Copyright (c) 2007 by Nuvoton Electronics Corporation                             */
/* All rights reserved                                                               */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/
/* File Contents:                                                                    */
/*   uart.c                                                                          */
/*                                                                                   */
/* This file contains:                                                               */
/*                                                                                   */
/* Project:                                                                          */
/*   .                                                                               */
/*                                                                                   */
/* Remark:                                                                           */
/*   1. Modem loop-back & flow control are alternative in uart1 ISR.                 */
/*                                                                                   */
/*   2. UART1 H/W & S/W flow control function have to perform on TX/RX interrupt     */
/*      mode. And RX trigger level must be 1 byte trigger.                           */
/*                                                                                   */
/*   # H/W flow control:                                                             */
/*     CTS(I), detect high when slave buffer full. Program should suspend TX.        */
/*     RTS(O), set high when RX buffer will full.                                    */
/*                                                                                   */
/*   # S/W flow control:                                                             */
/*     Xon, 0x11. Start TX or RX.                                                    */
/*     Xoff, 0x13. Stop TX or RX.                                                    */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/ 

#ifdef ECOS 
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "drv_api.h"
#include "diag.h"
#include "wbio.h"
#else 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "wblib.h"
#endif

#include "nuc900_reg.h"
#include "nuc900_uart.h"

/*-----------------------------------------*/
/* marco, type and constant definitions    */
/*-----------------------------------------*/
/*
	Define debug level
*/
//#define UART_DEBUG
//#define UART_FLOWCONTROL_DEBUG
//#define UART1_DEBUG  /* for UART1 high speed test */
//#define UART2_DEBUG  /* for UART2 IrDA test       */

#ifdef UART_DEBUG
#ifdef ECOS
#define UDEBUG			diag_printf
#else
#define UDEBUG			sysprintf
#endif  /* ECOS */
#else
#define UDEBUG(...)
#endif  /* UART_DEBUG */

#ifdef UART_FLOWCONTROL_DEBUG
#ifdef ECOS
#define FDEBUG			diag_printf
#else
#define FDEBUG			sysprintf
#endif  /* ECOS */
#else
#define FDEBUG(...)
#endif  /* UART_FLOWCONTROL_DEBUG */

#ifdef UART1_DEBUG
#ifdef ECOS
#define U1DEBUG			diag_printf
#else
#define U1DEBUG			sysprintf
#endif  /* ECOS */
#else
#define U1DEBUG(...)
#endif  /* UART1_DEBUG */

#ifdef UART2_DEBUG
#ifdef ECOS
#define U2DEBUG			diag_printf
#else
#define U2DEBUG			sysprintf
#endif  /* ECOS */
#else
#define U2DEBUG(...)
#endif  /* UART1_DEBUG */

/*-----------------------------------------*/
/* global file scope (static) variables    */
/*-----------------------------------------*/
static UART_BUFFER_T UART_DEV[UART_NUM];

static UINT32 UARTTXBUFSIZE[UART_NUM] = {500, 500, 500, 500, 500};  /* UART0, 1, 2, 3, 4 Tx buffer size */
static UINT32 UARTRXBUFSIZE[UART_NUM] = {500, 500, 500, 500, 500};	/* UART0, 1, 2, 3, 4 Rx buffer size */

/*
	IrDA register settings
*/
static BOOL _uart_bIsPerformIrDA = FALSE;  /* Default UART channel 2, IrDA function disable */
static UINT32 _uart_IrDATxReg = (IRCR_IrDAEN | IRCR_TXSELECT | IRCR_INVRX);
static UINT32 _uart_IrDARxReg = (IRCR_IrDAEN | IRCR_INVRX);

/*
	UART flag declarations.
*/
static volatile CHAR _uart_cDSRState1 = 0;   /* set 1, state change                         */
static volatile CHAR _uart_cDSRState3 = 0;   /* set 1, state change                         */
static volatile CHAR _uart_cBIIState_0 = 0;  /* set 1, UART channel 0 break interrupt occur */
static volatile CHAR _uart_cBIIState_1 = 0;  /* set 1, UART channel 1 break interrupt occur */
static volatile CHAR _uart_cBIIState_2 = 0;  /* set 1, UART channel 2 break interrupt occur */
static volatile CHAR _uart_cBIIState_3 = 0;  /* set 1, UART channel 3 break interrupt occur */
static volatile CHAR _uart_cBIIState_4 = 0;  /* set 1, UART channel 4 break interrupt occur */
static volatile CHAR _uart_cCTSState = 0;    /* set 1, state change                         */

/* 
	Define flow control flags & parameters.
*/
#define HWFLOWCONTROL	1
#define SWFLOWCONTROL	2
static volatile CHAR _uart_cFlowControlMode = 0;  /* default no flow control */
static volatile CHAR _uart_cHWTXStopped = 0;      /* Use for H/W flow control. Set 1, stop TX. Set 0, start TX.   */
static volatile CHAR _uart_cHWRXStopped = 0;      /* Use for H/W flow control. Set 1, stop RX. Set 0, start RX.   */
static volatile CHAR _uart_cSWTXStopped = 0;      /* Use for S/W flow control. Set 1, rec Xoff. Set 0, rec Xon.   */
static volatile CHAR _uart_cSWRXStopped = 0;      /* Use for S/W flow control. Set 1, send Xoff. Set 0, send Xon. */
static INT _uart_nMaxRxBuf = 0;                   /* used in uartReceiveChars() */
static INT _uart_nMinRxBuf = 0;                   /* used in uartReadRxBuf()    */

#ifdef ECOS
cyg_interrupt  uart0_int;
cyg_handle_t   uart0_int_handle;
cyg_interrupt  uart1_int;
cyg_handle_t   uart1_int_handle;
cyg_interrupt  uart2_int;
cyg_handle_t   uart2_int_handle;
cyg_interrupt  uart3_int;
cyg_handle_t   uart3_int_handle;
cyg_interrupt  uart4_int;
cyg_handle_t   uart4_int_handle;
#endif

/*-----------------------------------------*/
/* prototypes of static functions          */
/*-----------------------------------------*/
static UINT32 _uartTxBufGetNextOne(INT nNum, UINT32 uPointer);
static UINT32 _uartRxBufGetNextOne(INT nNum, UINT32 uPointer);
static VOID _uartEnableInterrupt(INT nNum, UINT32 uVal);
static VOID _uartDisableInterrupt(INT nNum, UINT32 uVal);
static VOID _uartReceiveChars(INT nNum);
static VOID _uartTransmitChars(INT nNum);
static VOID _uartCheckModemStatus(VOID);
static INT _uartSetBaudRate(UART_T *val);
static VOID _uartInstallISR(UINT8 ucNum);
static BOOL _uartBUFSpaceAlloc(INT nNum);
static BOOL _uartCheckTxBufSpace(INT nNum, UINT32 uHead, UINT32 uTail, UINT32 uLen);
static INT32 _uartReadRxBuf(INT nNum, PUINT8 pucBuf, UINT32 uLen);
static VOID _uartWriteTxBuf(INT nNum, PUINT8 pucBuf, UINT32 uLen);
static VOID _uartConfigureGPIO(UINT8 ucNum);
static INT _uartConfigureUART(PVOID pvParam);
static INT _uartPerformIrDA(UINT32 uCmd, UINT32 uCmd1);
static INT _uartGetRegisterValue(INT nNum, PVOID pvReg);	        


//------------------------- Program -------------------------//
#ifdef ECOS
static cyg_uint32 uart0ISR(cyg_vector_t vector, cyg_addrword_t data)
#else
VOID uart0ISR(VOID)
#endif
{
	UINT32 volatile uRegIIR, uRegLSR;

	uRegIIR = inpb(REG_UART0_IIR) & 0x0f;
	
	if(uRegIIR == IIR_THRE)  /* TX empty interrupt, check LSR 4 kinds of error further */
		_uartTransmitChars(UART0);
	
	if( (uRegIIR == IIR_RDA) || (uRegIIR == IIR_TOUT) )  /* Received Data Available interrupt */
		_uartReceiveChars(UART0);
		
	if(uRegIIR == IIR_RLS)	
	{
		uRegLSR = inpb(REG_UART0_LSR);
		if(uRegLSR & LSR_BII)
			_uart_cBIIState_0 = 1;			
	}
	
#ifdef ECOS
	cyg_drv_interrupt_acknowledge(vector);
	return CYG_ISR_HANDLED;
#endif			
}	

#ifdef ECOS
static cyg_uint32 uart1ISR(cyg_vector_t vector, cyg_addrword_t data)
#else
VOID uart1ISR(VOID)
#endif
{
	UINT32 volatile uRegIIR, uRegLSR, uRegMSR;

	uRegIIR = inpb(REG_UART1_IIR) & 0x0f;
	
	if(uRegIIR == IIR_THRE)  /* TX empty interrupt */
	{
		if( (!_uart_cHWTXStopped) && (!_uart_cSWTXStopped) )  
			_uartTransmitChars(UART1);
		else
		{
			_uartDisableInterrupt(UART1, IER_THRE);  /* disable TX empty interrupt */
			if(_uart_cFlowControlMode == HWFLOWCONTROL)	
				FDEBUG("H/W flow control, disable THRE\n");
			else
				FDEBUG("S/W flow control, disable THRE\n");	
		}	
	}
	
	if( (uRegIIR == IIR_RDA) || (uRegIIR == IIR_TOUT) )  /* Received Data Available interrupt */
		_uartReceiveChars(UART1);	
	
	if(uRegIIR == IIR_MOS)
	{
		if (_uart_cFlowControlMode == 0)
		{
			uRegMSR = inpb(REG_UART1_MSR) & 0x33;  /* Get DSR# CTS#, DDSR DCTS value */			
			if(uRegMSR & 0x02)
				_uart_cDSRState1 = 1;
			if (uRegMSR & 0x01)
				_uart_cCTSState = 1;			
		}
		else
			_uartCheckModemStatus();  /* H/W flow control */
	}
		
	if(uRegIIR == IIR_RLS)	
	{
		uRegLSR = inpb(REG_UART1_LSR);
		U1DEBUG("U1 Irpt_RLS [0x%x]!\n", uRegLSR); 
		
		if(uRegLSR & LSR_BII)
			_uart_cBIIState_1 = 1;			
			
		if (uRegLSR & LSR_OEI)  
			U1DEBUG("U1 OEI!\n");  
	}	
	
#ifdef ECOS
	cyg_drv_interrupt_acknowledge(vector);
	return CYG_ISR_HANDLED;
#endif			
}	

#ifdef ECOS
static cyg_uint32 uart2ISR(cyg_vector_t vector, cyg_addrword_t data)
#else
VOID uart2ISR(VOID)
#endif
{
	UINT32 volatile uRegIIR, uRegLSR;

	uRegIIR = inpb(REG_UART2_IIR) & 0x0f;
	
	if(uRegIIR == IIR_THRE)  /* TX empty interrupt, check LSR 4 kinds of error further */
		_uartTransmitChars(UART2);
	
	if( (uRegIIR == IIR_RDA) || (uRegIIR == IIR_TOUT) )  /* Received Data Available interrupt */
		_uartReceiveChars(UART2);		
		
	if(uRegIIR == IIR_RLS)	
	{
		uRegLSR = inpb(REG_UART2_LSR);
		U2DEBUG("U2 Irpt_RLS [0x%x]!\n", uRegLSR); 

		if(uRegLSR & LSR_BII)
			_uart_cBIIState_2 = 1;
			
		if (uRegLSR & LSR_OEI)  
			U2DEBUG("U2 OEI!\n");		
	}	
	
#ifdef ECOS
	cyg_drv_interrupt_acknowledge(vector);
	return CYG_ISR_HANDLED;
#endif			
}	

#ifdef ECOS
static cyg_uint32 uart3ISR(cyg_vector_t vector, cyg_addrword_t data)
#else
VOID uart3ISR(VOID)
#endif
{
	UINT32 volatile uRegIIR, uRegMSR, uRegLSR;

	uRegIIR = inpb(REG_UART3_IIR) & 0x0f;
	
	if(uRegIIR == IIR_THRE)  /* TX empty interrupt, check LSR 4 kinds of error further */
		_uartTransmitChars(UART3);
	
	if( (uRegIIR == IIR_RDA) || (uRegIIR == IIR_TOUT) )  /* Received Data Available interrupt */
		_uartReceiveChars(UART3);
		
	if(uRegIIR == IIR_MOS)  /* MODEM interrupt */
	{
		uRegMSR = inpb(REG_UART3_MSR) & 0x22;  /* Get DSR#, DDSR value */			
		if(uRegMSR & 0x02)
			_uart_cDSRState3 = 1;
	}	
	
	if(uRegIIR == IIR_RLS)	
	{
		uRegLSR = inpb(REG_UART3_LSR);
		if(uRegLSR & LSR_BII)
			_uart_cBIIState_3 = 1;			
	}
	
#ifdef ECOS
	cyg_drv_interrupt_acknowledge(vector);
	return CYG_ISR_HANDLED;
#endif				
}

#ifdef ECOS
static cyg_uint32 uart4ISR(cyg_vector_t vector, cyg_addrword_t data)
#else
VOID uart4ISR(VOID)
#endif
{
	UINT32 volatile uRegIIR, uRegLSR;

	uRegIIR = inpb(REG_UART4_IIR) & 0x0f;
	
	if(uRegIIR == IIR_THRE)  /* TX empty interrupt, check LSR 4 kinds of error further */
		_uartTransmitChars(UART4);
	
	if( (uRegIIR == IIR_RDA) || (uRegIIR == IIR_TOUT) )  /* Received Data Available interrupt */
		_uartReceiveChars(UART4);
		
	if(uRegIIR == IIR_RLS)	
	{
		uRegLSR = inpb(REG_UART4_LSR);
		if(uRegLSR & LSR_BII)
			_uart_cBIIState_4 = 1;			
	}
	
#ifdef ECOS
	cyg_drv_interrupt_acknowledge(vector);
	return CYG_ISR_HANDLED;
#endif			
}

static UINT32 _uartTxBufGetNextOne(INT nNum, UINT32 uPointer)    
{
	if((uPointer + 1) == UARTTXBUFSIZE[nNum])
		return NULL;
	else
		return (uPointer + 1);	
}

static UINT32 _uartRxBufGetNextOne(INT nNum, UINT32 uPointer)    
{
	if((uPointer + 1) == UARTRXBUFSIZE[nNum])
		return NULL;
	else
		return (uPointer + 1);	
}

static VOID _uartEnableInterrupt(INT nNum, UINT32 uVal)
{
	UINT32 uReg = 0;
	
	uReg = inpw(REG_UART0_IER+(nNum * UARTOFFSET));
	uReg |= uVal;
	outpb(REG_UART0_IER+(nNum * UARTOFFSET), uReg);
}

static VOID _uartDisableInterrupt(INT nNum, UINT32 uVal)
{
	UINT32 uReg = 0;

	if(uVal == DISABLEALLIER)
		outpb(REG_UART0_IER+(nNum * UARTOFFSET), 0);
	else
	{	
		uReg = inpw(REG_UART0_IER+(nNum * UARTOFFSET));
		uReg &= ~uVal;
		outpb(REG_UART0_IER+(nNum * UARTOFFSET), uReg);
	}
}	

static VOID _uartReceiveChars(INT nNum)
{
	UINT32 volatile uRegLSR, uBuf = 0;
	INT nMaxCount = 256;
	UCHAR ucChar;
	
	UART_BUFFER_T *dev;
	
	dev = (UART_BUFFER_T *) &UART_DEV[nNum];
	
	uRegLSR = inpb(REG_UART0_LSR+(nNum * UARTOFFSET));
	
	do {
		uBuf = _uartRxBufGetNextOne(nNum, dev->uUartRxTail);
		if(uBuf == dev->uUartRxHead)  /* Rx buffer full */
		{
			ucChar = inpb(REG_UART0_RBR+(nNum * UARTOFFSET));
			
			//if (_uart_cHWRXStopped)
				U1DEBUG("[%d] buf full!\n", nNum);  
			
			break;  
		}	
		
		ucChar = inpb(REG_UART0_RBR+(nNum * UARTOFFSET));
		
		/* S/W flow control function */
		if(nNum == UART1)
		{
			if(_uart_cFlowControlMode == SWFLOWCONTROL)
			{
				if(ucChar == 0x13)  /* Xoff, TX stopped */
				{
					_uart_cSWTXStopped = 1;
					FDEBUG("S/W flow control, TX stopped\n");
					break;
				}				
				if(ucChar == 0x11)  /* Xon, TX started */
				{
					_uartEnableInterrupt(UART1, IER_THRE);  /* Important. If TX buf is full, should send first. */
					_uart_cSWTXStopped = 0;
					FDEBUG("S/W flow control, TX started\n");
					break;	
				}
			}		
		}
        /* End of S/W flow control function */			
		
		dev->pucUartRxBuf[dev->uUartRxTail] = ucChar;
				
		/* Check LSR for BII, FEI, PEI, OEI */
		dev->pucUARTFlag[dev->uUartRxTail] = 0;  
		
		if(uRegLSR & LSR_BII)
		{
			dev->pucUARTFlag[dev->uUartRxTail] = UART_BII;
			U1DEBUG("BII!\n");
		}
		else if(uRegLSR & LSR_FEI)
		{
			dev->pucUARTFlag[dev->uUartRxTail] = UART_FEI;	
			U1DEBUG("FEI!\n");
		}
		else if(uRegLSR & LSR_PEI)
		{
			dev->pucUARTFlag[dev->uUartRxTail] = UART_PEI;	
			U1DEBUG("PEI!\n");
		}
		else if (uRegLSR & LSR_OEI)  
			U1DEBUG("OEI!\n");			
			
		dev->uUartRxTail = _uartRxBufGetNextOne(nNum, dev->uUartRxTail);
		dev->uRecCnt++;
		
		/* H/W flow control function of RTS */ 
		if(nNum == UART1)
		{
			if(_uart_cFlowControlMode == HWFLOWCONTROL)
			{
				if(dev->uRecCnt >= _uart_nMaxRxBuf)
				{ 
					outpb(REG_UART1_MCR, inpb(REG_UART1_MCR) & ~0x02);  /* set RTS signal high */
					_uart_cHWRXStopped = 1;                             /* RX stopped          */					 
					FDEBUG("H/W flow control, RX stopped\n");
				}
			}	
			if(_uart_cFlowControlMode == SWFLOWCONTROL)
			{
				if(dev->uRecCnt >= _uart_nMaxRxBuf)  /* send Xoff (0x13) */
				{ 
					/* Wait until the transmitter buffer is empty */
		   			while( !(inpb(REG_UART1_LSR) & LSR_THRE) );  
		   			outpb(REG_UART1_THR, 0x13);	
		   			
		   			_uart_cSWRXStopped = 1;
		   			FDEBUG("S/W flow control, RX stopped\n");
				}
			}			
		}
		/* End of H/W flow control function of RTS */			
			
		/* overrun error is special case, H/W ignore the character */	
		if(uRegLSR & LSR_OEI)
		{
			dev->pucUARTFlag[dev->uUartRxTail] = UART_OEI;
			dev->uUartRxTail = _uartRxBufGetNextOne(nNum, dev->uUartRxTail);
			dev->uRecCnt++;	
		}		
		
		uRegLSR = inpb(REG_UART0_LSR+(nNum * UARTOFFSET));
	}while((uRegLSR & LSR_RFDR) && (nMaxCount-- > 0));	

}	

static VOID _uartTransmitChars(INT nNum)
{
	UINT32 volatile i;
	
	UART_BUFFER_T *dev;
	
	dev = (UART_BUFFER_T *) &UART_DEV[nNum];
	
	if(dev->uUartTxHead != dev->uUartTxTail)  /* buffer is not empty */
	{
		for (i=0; i<8; i++)  
		{
			outpb(REG_UART0_THR+(nNum * UARTOFFSET), dev->pucUartTxBuf[dev->uUartTxHead]);	
			dev->uUartTxHead = _uartTxBufGetNextOne(nNum, dev->uUartTxHead);
			
			if(dev->uUartTxHead == dev->uUartTxTail)  /* buffer empty */
			{
				_uartDisableInterrupt(nNum, IER_THRE);
				break;
			}
		}
	}
}	

/* 
	Call by uart1ISR().   
*/
static VOID _uartCheckModemStatus(VOID)
{
	UINT32 volatile uRegMSR;
	
	UART_BUFFER_T *dev;
	
	dev = (UART_BUFFER_T *) &UART_DEV[UART1];
	
	FDEBUG("\n Modem INT\n");  
	uRegMSR = inpw(REG_UART1_MSR);
	if(_uart_cHWTXStopped)
	{
		if(uRegMSR & 0x10)  /* CTS high, external signal is low */
		{
			_uart_cHWTXStopped = 0;
			FDEBUG("H/W flow control ...\n");
			
			/* 2007.11.12 modify, PT23 HHWu */			
			if(dev->uUartTxHead != dev->uUartTxTail)    /* buffer is not empty */
			{
				_uartEnableInterrupt(UART1, IER_THRE);  /* enable TX empty interrupt */
				FDEBUG("buf not empty, TX continued\n");
			}
		}	
	}
	else
	{
		if( !(uRegMSR & 0x10) )  /* CTS low, external signal is high */
		{
			_uart_cHWTXStopped = 1;	
			_uartDisableInterrupt(UART1, IER_THRE);  /* disable TX empty interrupt */
			FDEBUG("H/W flow control, TX stopped\n");			
		}
	}
}

static INT _uartSetBaudRate(UART_T *val)
{
	UINT32 uDivisor, uMaxBaud;
	
	if (val->uFreq > 200000000)  /* Max frequency 200MHz */
		return -1;
		
	uMaxBaud = (val -> uFreq) / 32;
	if (val -> uBaudRate > uMaxBaud)
		return -1;	

    /* First, compute the baudrate divisor. */
    uDivisor = (val->uFreq / (val->uBaudRate * 16));
    
    if ((val->uFreq % (val->uBaudRate * 16)) > ((val->uBaudRate * 16) / 2))
        uDivisor++;
        
    uDivisor -= 2;

    /* Set the divisor latch bit. */
    outpb(REG_UART0_LCR+(val->ucUartNo * UARTOFFSET), 0x80);

    /* First write the LSB of the baud rate divisor. */
    outpb(REG_UART0_DLL+(val->ucUartNo * UARTOFFSET), (UINT8)uDivisor);

    /* Now write the MSB of the baud rate divisor. */
    outpb(REG_UART0_DLM+(val->ucUartNo * UARTOFFSET), (UINT8)(uDivisor >> 8));

    /* Now that the baud rate has been set turn off Divisor Latch
       to reference data in line control */
    outpb(REG_UART0_LCR+(val->ucUartNo * UARTOFFSET), 0);
    
    return 0;
}	

static VOID _uartInstallISR(UINT8 ucNum)   
{
	UART_BUFFER_T *dev;
	
	dev = (UART_BUFFER_T *) &UART_DEV[ucNum];	
	
	switch(ucNum)
	{
		case UART0:
#ifdef ECOS	
			cyg_drv_interrupt_create(CYGNUM_HAL_INTERRUPT_UART0_TX, 99, 0, uart0ISR, NULL, &uart0_int_handle, &uart0_int);
		    cyg_drv_interrupt_attach(uart0_int_handle);
		    cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_UART0_TX);
#else			
			dev->pvUartVector = sysInstallISR(IRQ_LEVEL_1, IRQ_UART0, (PVOID)uart0ISR);
			sysEnableInterrupt(IRQ_UART0);
#endif			
			break;
		
		case UART1:
#ifdef ECOS	
			cyg_drv_interrupt_create(CYGNUM_HAL_INTERRUPT_UART1_TX, 99, 0, uart1ISR, NULL, &uart1_int_handle, &uart1_int);
		    cyg_drv_interrupt_attach(uart1_int_handle);
		    cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_UART1_TX);
#else		
			dev->pvUartVector = sysInstallISR(IRQ_LEVEL_1, IRQ_UART1, (PVOID)uart1ISR);
			sysEnableInterrupt(IRQ_UART1);
#endif			
			break;
		
		case UART2:
#ifdef ECOS	
			cyg_drv_interrupt_create(CYGNUM_HAL_INTERRUPT_UART2_TX, 99, 0, uart2ISR, NULL, &uart2_int_handle, &uart2_int);
		    cyg_drv_interrupt_attach(uart2_int_handle);
		    cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_UART2_TX);
#else		
			dev->pvUartVector = sysInstallISR(IRQ_LEVEL_1, IRQ_UART2, (PVOID)uart2ISR);
			sysEnableInterrupt(IRQ_UART2);
#endif			
			break;
		
		case UART3:
#ifdef ECOS	
			cyg_drv_interrupt_create(CYGNUM_HAL_INTERRUPT_UART3_TX, 99, 0, uart3ISR, NULL, &uart3_int_handle, &uart3_int);
		    cyg_drv_interrupt_attach(uart3_int_handle);
		    cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_UART3_TX);
#else		
			dev->pvUartVector = sysInstallISR(IRQ_LEVEL_1, IRQ_UART3, (PVOID)uart3ISR);
			sysEnableInterrupt(IRQ_UART3);
#endif			
			break;
			
		case UART4:
#ifdef ECOS	
			cyg_drv_interrupt_create(CYGNUM_HAL_INTERRUPT_UART4_TX, 99, 0, uart4ISR, NULL, &uart4_int_handle, &uart4_int);
		    cyg_drv_interrupt_attach(uart4_int_handle);
		    cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_UART4_TX);
#else		
			dev->pvUartVector = sysInstallISR(IRQ_LEVEL_1, IRQ_UART4, (PVOID)uart4ISR);
			sysEnableInterrupt(IRQ_UART4);
#endif			
			break;	
		
		default:	
			break;
	}

#ifndef ECOS
    sysSetLocalInterrupt(ENABLE_IRQ);
#endif    
}	

static BOOL _uartBUFSpaceAlloc(INT nNum)   
{
	UART_BUFFER_T *dev;
	
	dev = (UART_BUFFER_T *) &UART_DEV[nNum];

	/* Memory allocate Tx buffer */
	dev->pucUartTxBuf = (PUINT8) malloc(UARTTXBUFSIZE[nNum] * sizeof(UINT8));
	if(dev->pucUartTxBuf == NULL)
		return FALSE;
		
	/* Memory allocate Rx buffer */	
	dev->pucUartRxBuf = (PUINT8) malloc(UARTRXBUFSIZE[nNum] * sizeof(UINT8));
	if(dev->pucUartRxBuf == NULL)
	{
		free(dev->pucUartTxBuf);
		return FALSE;
	}	
	
	/* Memory allocate Rx character flag */
	dev->pucUARTFlag = (PINT) malloc(UARTRXBUFSIZE[nNum] * sizeof(INT));
	if(dev->pucUARTFlag == NULL)
	{
		free(dev->pucUartTxBuf);
		free(dev->pucUartRxBuf);
		return FALSE;
	}
	
	/* initial memory */
	memset(dev->pucUartTxBuf, 0, UARTTXBUFSIZE[nNum] * sizeof(UINT8));
	memset(dev->pucUartRxBuf, 0, UARTRXBUFSIZE[nNum] * sizeof(UINT8));
	memset(dev->pucUARTFlag, 0, UARTRXBUFSIZE[nNum] * sizeof(INT));
	
	/* inital struct UART_BUFFER_STRUCT, uUartTxHead, uUartTxTail, uUartRxHead, uUartRxTail */	
	dev->uUartTxHead = dev->uUartTxTail = NULL;
	dev->uUartRxHead = dev->uUartRxTail = NULL;

	return TRUE;	
}	

static BOOL _uartCheckTxBufSpace(INT nNum, UINT32 uHead, UINT32 uTail, UINT32 uLen)  
{
	UINT32 uBuf;
	
	uBuf = _uartTxBufGetNextOne(nNum, uTail);
	if(uBuf == uHead)   /* Tx buffer full */
		return FALSE;
		
	if(uHead == uTail)  /* Tx buffer empty */	
		return TRUE;
		
	if(uTail > uHead)
	{
		if(uLen >= (UARTTXBUFSIZE[nNum]-(uTail-uHead)))  /* 2007.10.29 fix pointer bug, PT23 HHWu */
			return FALSE;  /* Tx buffer space isn't enough */	
		else
			return TRUE;	
	}	
	else  
	{
		/* case: uTail < uHead */
		if(uLen >= (uHead-uTail))  /* 2007.10.29 fix pointer bug, PT23 HHWu */
			return FALSE;  /* Tx buffer space isn't enough */		
		else
			return TRUE;	
	}				
	
	return TRUE; 
}

static INT32 _uartReadRxBuf(INT nNum, PUINT8 pucBuf, UINT32 uLen)
{
	UINT32 i;
		
	UART_BUFFER_T *dev;
	
	dev = (UART_BUFFER_T *) &UART_DEV[nNum];
	
	if(dev->bIsUseUARTRxInt == TRUE)
	{
	
		// disable Rx interrupt ...
	
		if(dev->uRecCnt == 0)  /* no data in Rx buffer */
			return 0;
		
		if(uLen > dev->uRecCnt)	
			uLen = dev->uRecCnt;
		
		for(i = uLen ; i > 0 ; i--)
		{
			*pucBuf++ = dev->pucUartRxBuf[dev->uUartRxHead];
			dev->uUartRxHead = _uartRxBufGetNextOne(nNum, dev->uUartRxHead);
		}		
	
		dev->uRecCnt -= uLen;  /* maintain uRecCnt value */	
	
		// enable Rx interrupt ...
		
		/* H/W & S/W flow control function */		
		if(nNum == UART1)
		{
			if(_uart_cFlowControlMode == HWFLOWCONTROL)
			{
				if( (dev->uRecCnt <= _uart_nMinRxBuf) && (_uart_cHWRXStopped == 1) )
				{
					outpb(REG_UART1_MCR, inpb(REG_UART1_MCR) | 0x02);	 /* output RTS low */
					_uart_cHWRXStopped = 0;      /* RX started     */
					FDEBUG("H/W flow control, RX started\n");
				}
			}
			if(_uart_cFlowControlMode == SWFLOWCONTROL)
			{
				if( (dev->uRecCnt <= _uart_nMinRxBuf) && (_uart_cSWRXStopped == 1) )
				{
					/* Wait until the transmitter buffer is empty */
		   			while( !(inpb(REG_UART1_LSR) & LSR_THRE) );
		   			outpb(REG_UART1_THR, 0x11);  /* send Xon, RX started	*/					
					_uart_cSWRXStopped = 0;
					FDEBUG("S/W flow control, RX started\n");
				}
			}			
		}
		/* End of H/W & S/W flow control function */	
	}
	else  /* pooling mode */
	{
		for(i = 0 ; i < uLen; i++)
		{
			while(!(inpb(REG_UART0_LSR+(nNum * UARTOFFSET)) & LSR_RFDR));
			*pucBuf++ = inpb(REG_UART0_RBR+(nNum * UARTOFFSET));
		}
	}	
		
	return (uLen);
}	

static VOID _uartWriteTxBuf(INT nNum, PUINT8 pucBuf, UINT32 uLen)   
{
	UINT32 i;
	
	UART_BUFFER_T *dev;
	
	dev = (UART_BUFFER_T *) &UART_DEV[nNum];
	
	/* Check interrupt or polling mode first */	
	if(dev->bIsUseUARTTxInt == TRUE)
	{
		while(uLen--)
		{
			dev->pucUartTxBuf[dev->uUartTxTail] = *pucBuf++;
			dev->uUartTxTail = _uartTxBufGetNextOne(nNum, dev->uUartTxTail);
		}	
		
    	if (!(inpb(REG_UART0_IER+(nNum * UARTOFFSET)) & IER_THRE))  /* Enable Tx empty interrupt */
    		_uartEnableInterrupt(nNum, IER_THRE);	
	}	
	else  /* pooling mode */
	{
		for(i = 0 ; i < uLen ; i++)
		{
			/* Wait until the transmitter buffer is empty */
   			while(!(inpb(REG_UART0_LSR+(nNum * UARTOFFSET)) & LSR_THRE));
   			outpb(REG_UART0_THR+(nNum * UARTOFFSET), *pucBuf++);	
		}
	}
}

static VOID _uartConfigureGPIO(UINT8 ucNum)	
{
	UINT32 uValue = 0;
	
	switch(ucNum)
	{
		case UART0:	
			/* Configure GPIOE[1:0] as UART0, TXD0 & RXD0. */
			uValue = inpw(REG_MFSEL) & ~( (1 << 8) | (1 << 13) );
			outpw(REG_MFSEL, uValue | (1 << 8));
			break;
		
		case UART1:	
			/* 
				Configure GPIOE[5:2] as UART1(Bluetooth), TXD1 & RXD1 & RTS1 & CTS1. 
				Set full modem by ioctl, UART_IOC_SETUART1FULLMODEM. 
			*/
			uValue = inpw(REG_MFSEL) & ~( (1 << 9) | (1 << 13) );
			outpw(REG_MFSEL, uValue | (1 << 9));
			break;
				
		case UART2:
			/* Configure GPIOE[7:6] as UART2(IrDA), TXD2 & RXD2. */
			uValue = inpw(REG_MFSEL) & ~( (1 << 10) | (1 << 13) );
			outpw(REG_MFSEL, uValue | (1 << 10));
			break;
				
		case UART3:	
			/* Configure GPIOE[11:8] as UART3(Modem), TXD3 & RXD3 & DTR3 & DSR3. */
			uValue = inpw(REG_MFSEL) & ~( (1 << 11) | (1 << 13) );
			outpw(REG_MFSEL, uValue | (1 << 11));
			break;
				
		case UART4:
			/* Configure GPIOE[13:12] as UART4, TXD4 & RXD4. */
			uValue = inpw(REG_MFSEL) & ~( (1 << 12) | (1 << 13) );
			outpw(REG_MFSEL, uValue | (1 << 12));
			break;	
		
		default:	
			break;	
	}	
}	

static INT _uartConfigureUART(PVOID pvParam)
{
	INT retval;
	BOOL bIsMemoryAllocOk;
	
	UART_T *param = (UART_T *) pvParam;
	
	/* Check UART channel */
	if ( (param->ucUartNo != UART0) && 
	     (param->ucUartNo != UART1) &&
	     (param->ucUartNo != UART2) &&
	     (param->ucUartNo != UART3) &&
	     (param->ucUartNo != UART4) )
	    return UART_ERR_CHANNEL_INVALID; 
	
    /* Check the supplied parity */
    if ( (param->ucParity != PARITY_NONE) &&
         (param->ucParity != PARITY_EVEN) &&
         (param->ucParity != PARITY_ODD)  &&
         (param->ucParity != (PARITY_ODD | PARITY_STICK)) && 
         (param->ucParity != (PARITY_EVEN | PARITY_STICK)) ) 
        return UART_ERR_PARITY_INVALID;

    /* Check the supplied number of data bits */
    if ( (param->ucDataBits != DATA_BITS_5) &&
         (param->ucDataBits != DATA_BITS_6) &&
         (param->ucDataBits != DATA_BITS_7) &&
         (param->ucDataBits != DATA_BITS_8) )
        return UART_ERR_DATA_BITS_INVALID;

    /* Check the supplied number of stop bits */
    if ( (param->ucStopBits != STOP_BITS_1) &&
         (param->ucStopBits != STOP_BITS_2) )
        return UART_ERR_STOP_BITS_INVALID;

	/* Check the supplied nember of trigger level bytes */
	if (param -> ucUartNo == UART1)
	{
	 	/* UART1 */
	 	if ( (param->ucRxTriggerLevel != UART1_LEVEL_1_BYTE)   &&
	         (param->ucRxTriggerLevel != UART1_LEVEL_4_BYTES)  &&
	         (param->ucRxTriggerLevel != UART1_LEVEL_8_BYTES)  &&
	         (param->ucRxTriggerLevel != UART1_LEVEL_14_BYTES) &&
	         (param->ucRxTriggerLevel != UART1_LEVEL_30_BYTES) &&
	         (param->ucRxTriggerLevel != UART1_LEVEL_46_BYTES) &&
	         (param->ucRxTriggerLevel != UART1_LEVEL_62_BYTES) )
	        return UART_ERR_TRIGGERLEVEL_INVALID;
	}
	else
	{ 
		/* UART0, UART2, UART3, UART4 */
	    if ( (param->ucRxTriggerLevel != LEVEL_1_BYTE)  &&
	         (param->ucRxTriggerLevel != LEVEL_4_BYTES) &&
	         (param->ucRxTriggerLevel != LEVEL_8_BYTES) &&
	         (param->ucRxTriggerLevel != LEVEL_14_BYTES) )
	        return UART_ERR_TRIGGERLEVEL_INVALID;
	}        
        
    /* Enable UART clock */
    switch(param->ucUartNo)
    {
    	case UART0:
    		outpw(REG_CLKEN, inpw(REG_CLKEN) | (1 << 11));
    		break;    		
    	case UART1:
    		outpw(REG_CLKEN, inpw(REG_CLKEN) | (1 << 12));
    		break;    		
    	case UART2:
    		outpw(REG_CLKEN, inpw(REG_CLKEN) | (1 << 13));
    		break;    		
    	case UART3:
    		outpw(REG_CLKEN, inpw(REG_CLKEN) | (1 << 14));
    		break;    	
    	case UART4:
    		outpw(REG_CLKEN, inpw(REG_CLKEN) | (1 << 15));
    		break;
    	
    	default:
    		break;	
    }
    
    /* Reset TX/RX FIFOs */
    outpb(REG_UART0_FCR+(param->ucUartNo * UARTOFFSET), 0x07);
       
    /* Setup baud rate */   
    retval = _uartSetBaudRate(param);
    if (retval < 0)
    	return UART_ERR_SET_BAUDRATE_FAIL;  
    
    /* Setup parity, data bits, and stop bits */
    outpb(REG_UART0_LCR+(param->ucUartNo * UARTOFFSET),
    	  (param->ucParity | param->ucDataBits | param->ucStopBits)); 
    	  
    /* Setup Rx time out value */
    outpb(REG_UART0_TOR+(param->ucUartNo * UARTOFFSET), 0x80+0x20);	   
        
	/* Setup FIFO trigger level */
	outpb(REG_UART0_FCR+(param->ucUartNo * UARTOFFSET), param->ucRxTriggerLevel);
	
	/* only exec once unless call uartClose() */
	if(UART_DEV[param->ucUartNo].bIsUARTInitial == FALSE)  
	{
		/* Configure GPIO function */
    	_uartConfigureGPIO(param->ucUartNo);
		
		/* Allocate Tx, Rx buffer */
		bIsMemoryAllocOk = _uartBUFSpaceAlloc(param->ucUartNo);
		if(bIsMemoryAllocOk == FALSE)
			return UART_ERR_ALLOC_MEMORY_FAIL;
			
		/* Hook UART interrupt service routine */
		_uartInstallISR(param->ucUartNo);
			
		/* Enable Rx interrupt */	
		if(UART_DEV[param->ucUartNo].bIsUseUARTRxInt == TRUE)
			_uartEnableInterrupt(param->ucUartNo, IER_RDA);
		
		/* inital struct UART_BUFFER_STRUCT, uRecCnt */	
		UART_DEV[param->ucUartNo].uRecCnt = 0;
	}		
	
	UART_DEV[param->ucUartNo].bIsUARTInitial = TRUE;  /* it's important to set TRUE */
    return 0;
}	

static INT _uartPerformIrDA(UINT32 uCmd, UINT32 uCmd1)  /* UART2 only */
{
	switch(uCmd)
	{
		case ENABLEIrDA:
			_uart_bIsPerformIrDA = TRUE;
			
			if(uCmd1 == IrDA_TX)
				outpb(REG_UART2_IRCR, _uart_IrDATxReg);
			else if(uCmd1 == IrDA_RX)
				outpb(REG_UART2_IRCR, _uart_IrDARxReg);
			else
				return UART_ERR_IrDA_COMMAND_INVALID;
			
			break;
			
		case DISABLEIrDA:
			_uart_bIsPerformIrDA = FALSE;
			outpb(REG_UART2_IRCR, 0x40);  /* Set default value, INV_TX set 0, INV_RX set 1 */
			break;	
		
		default:
			return UART_ERR_IrDA_COMMAND_INVALID;
	}
	
#ifdef UART2_DEBUG	
	_uartEnableInterrupt(UART2, IER_RLS); 	
#endif	
	
	return 0;
}	

/*
	Remark:
	1. LCR & LSR aren't support yet.
*/
static INT _uartGetRegisterValue(INT nNum, PVOID pvReg)
{
	INT nCnt = 0; 
	UINT32 uReg = 0;
	UINT32 uOffset = nNum * UARTOFFSET;
	
	UART_REGISTER_T *reg = (UART_REGISTER_T *) pvReg;
	
	memset(reg, 0, sizeof(UART_REGISTER_T));
			
	/* Read IER */		
	reg->uUartReg[nCnt][0] = REG_UART0_IER + uOffset;
	reg->uUartReg[nCnt++][1] = inpb(REG_UART0_IER + uOffset);
	
	/* Read DLL, DLM */		
	uReg = inpb(REG_UART0_LCR + uOffset);
	outpb(REG_UART0_LCR + uOffset, 0x80);  /* Set the divisor latch bit */
	reg->uUartReg[nCnt][0] = REG_UART0_DLL + uOffset;
	reg->uUartReg[nCnt++][1] = inpb(REG_UART0_DLL + uOffset);
	reg->uUartReg[nCnt][0] = REG_UART0_DLM + uOffset;
	reg->uUartReg[nCnt++][1] = inpb(REG_UART0_DLM + uOffset);
	outpb(REG_UART0_LCR + uOffset, 0);  /* Now that the baud rate has been set turn off Divisor Latch, to reference data in line control */
    outpb(REG_UART0_LCR + uOffset, uReg);                                   
                                                                                
    /* Read IIR */  
	reg->uUartReg[nCnt][0] = REG_UART0_LCR + uOffset;
	reg->uUartReg[nCnt++][1] = inpb(REG_UART0_LCR + uOffset);   
	
	/* Read MCR, MSR */                                   
	if(nNum == UART1) {
		reg->uUartReg[nCnt][0] = REG_UART1_MCR;
		reg->uUartReg[nCnt++][1] = inpb(REG_UART1_MCR);		
		reg->uUartReg[nCnt][0] = REG_UART1_MSR;
		reg->uUartReg[nCnt++][1] = inpb(REG_UART1_MSR);		
	} else if(nNum == UART3) {
		reg->uUartReg[nCnt][0] = REG_UART3_MCR;
		reg->uUartReg[nCnt++][1] = inpb(REG_UART3_MCR);		
		reg->uUartReg[nCnt][0] = REG_UART3_MSR;
		reg->uUartReg[nCnt++][1] = inpb(REG_UART3_MSR);		
	}	
	
	/* Read TOR */
	reg->uUartReg[nCnt][0] = REG_UART0_TOR + uOffset;
	reg->uUartReg[nCnt++][1] = inpb(REG_UART0_TOR + uOffset);	
	
	/* Read IRCR */
	if(nNum == UART2) {
		reg->uUartReg[nCnt][0] = REG_UART2_IRCR;
		reg->uUartReg[nCnt++][1] = inpb(REG_UART2_IRCR);	
	}		    
	
	return (nCnt);
}	

/*-----------------------------------------------------------------------------------*/
/* Function:                                                                         */
/*   uartInit                                                                        */
/*                                                                                   */
/* Parameters:                                                                       */
/*   None.                                                                           */
/*                                                                                   */                                                                           
/* Returns:                                                                          */
/*   0        Success.                                                               */
/*                                                                                   */
/* Side effects:                                                                     */
/*   None.                                                                           */
/*                                                                                   */
/* Description:                                                                      */
/*   Initialize some device struct parameters.                                       */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/

INT uartInit(VOID)   
{
	INT i;
	
	/* Initial UART_BUFFER_T struct */
	for(i = 0; i < UART_NUM ; i++)
		UART_DEV[i].bIsUARTInitial = FALSE;
		
	for(i = 0; i < UART_NUM ; i++)
		UART_DEV[i].bIsUseUARTTxInt = TRUE;	
	
	for(i = 0; i < UART_NUM ; i++)
		UART_DEV[i].bIsUseUARTRxInt = TRUE;
	
	for(i = 0; i < UART_NUM ; i++)
		UART_DEV[i].uRecCnt = 0;
		
	return 0;
}

/*-----------------------------------------------------------------------------------*/
/* Function:                                                                         */
/*   uartOpen                                                                        */
/*                                                                                   */
/* Parameters:                                                                       */
/*   uart              Point to UART variables struct that is configured by          */
/*                     application.                                                  */
/*                                                                                   */                                                                           
/* Returns:                                                                          */
/*   0                 Success.                                                      */
/*   UART_EIO        Configure UART fail.                                            */
/*                                                                                   */
/* Side effects:                                                                     */
/*   None.                                                                           */
/*                                                                                   */
/* Description:                                                                      */
/*   This function activates the UART channel 0~4.                                   */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/

INT uartOpen(PVOID uart)   
{
	INT nValue = 0;
	UART_T *dev = (UART_T *) uart; 
		
	if((nValue = _uartConfigureUART(uart)) < 0)
	{
		if(nValue != UART_ERR_CHANNEL_INVALID)
			UART_DEV[dev->ucUartNo].nErrno = nValue;
			
		return UART_EIO;	
	}	
	else
		UART_DEV[dev->ucUartNo].nErrno = 0;

    return 0;
}

/*-----------------------------------------------------------------------------------*/
/* Function:                                                                         */
/*   uartRead                                                                        */
/*                                                                                   */
/* Parameters:                                                                       */
/*   nNum			UART channel.                                                    */
/*   pucBuf			Receive buffer pointer.                                          */
/*   uLen			Receive buffer length.                                           */
/*                                                                                   */
/* Returns:                                                                          */
/*   > 0            Retrun read length on success.                                   */
/*   0				There is no data in RX buffer.                                   */
/*   UART_EIO       No activated or read fail.                                       */
/*	 UART_ENODEV	UART channel out of range.                                       */
/*                                                                                   */
/* Side effects:                                                                     */
/*   None.                                                                           */
/*                                                                                   */
/* Description:                                                                      */
/*   Read RX FIFO returned data or RX driver buffer.                                 */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/

INT32 uartRead(INT nNum, PUINT8 pucBuf, UINT32 uLen) 
{
	UART_BUFFER_T *dev;
	
	if((nNum < UART0) || (nNum > UART4))
		return UART_ENODEV;	
		
	dev = (UART_BUFFER_T *) &UART_DEV[nNum];			
	
	/* Check UART initial status */
	if(dev->bIsUARTInitial == FALSE)
		return UART_EIO;
		
	/* Check uLen value */
	if((uLen > UARTRXBUFSIZE[nNum]) || (uLen == 0))
		return UART_EIO;
	
	return (_uartReadRxBuf(nNum, pucBuf, uLen));	
}

/*-----------------------------------------------------------------------------------*/
/* Function:                                                                         */
/*   uartWrite                                                                       */
/*                                                                                   */
/* Parameters:                                                                       */
/*   nNum			 UART channel.                                                   */
/*   pucBuf			 Transmit buffer pointer.                                        */
/*   uLen			 Transmit buffer length.                                         */
/*                                                                                   */                                                                           
/* Returns:                                                                          */
/*   > 0             Return write length on success.                                 */
/*   UART_EIO        No activated or write fail.                                     */
/*   UART_ENODEV	 UART channel out of range.                                      */
/*                                                                                   */
/* Side effects:                                                                     */
/*   None.                                                                           */
/*                                                                                   */
/* Description:                                                                      */
/*   Write data to TX FIFO directly or TX driver buffer.                             */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/

INT32 uartWrite(INT nNum, PUINT8 pucBuf, UINT32 uLen)   
{
	BOOL bIsTxBufEnough;
	
	UART_BUFFER_T *dev;
	
	if((nNum < UART0) || (nNum > UART4))
		return UART_ENODEV;	
		
	dev = (UART_BUFFER_T *) &UART_DEV[nNum];
	dev->nErrno = 0;			
	
	/* Check UART initial status */
	if(dev->bIsUARTInitial == FALSE)
		return UART_EIO;
	
	/* Check uLen value */
	if((uLen > UARTWRITESIZE) || (uLen == 0))
		return UART_EIO;
		
	/* Check UART Tx buffer */
	if(dev->bIsUseUARTTxInt == TRUE)
	{
		bIsTxBufEnough = _uartCheckTxBufSpace(nNum, dev->uUartTxHead, dev->uUartTxTail, uLen);
		if(bIsTxBufEnough == FALSE)
		{
			//sysprintf("Tx buf not enough\n"); 
			dev->nErrno = UART_ERR_TX_BUF_NOT_ENOUGH;
			return UART_EIO;
		}	
	}	

	/* Move data to UART Tx buffer then transmit */
	_uartWriteTxBuf(nNum, pucBuf, uLen);

	return (uLen);
}	

/*-----------------------------------------------------------------------------------*/
/* Function:                                                                         */
/*   uartIoctl                                                                       */
/*                                                                                   */
/* Parameters:                                                                       */
/*   nNum               UART channel.                                                */
/*   uCmd               Command.                                                     */
/*   uArg0, uArg1       Arguments for the command.                                   */   
/*                                                                                   */                                                                           
/* Returns:                                                                          */
/*   0                  Success                                                      */
/*   UART_EIO           No activated or argument error or configure UART fail.       */
/*	 UART_ENODEV		UART channel out of range.                                   */
/*	 UART_ENOTTY		Command not support.                                         */
/*                                                                                   */
/* Side effects:                                                                     */
/*   None.                                                                           */
/*                                                                                   */
/* Description:                                                                      */
/*   Support some UART driver commands for application.                              */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/

INT uartIoctl(INT nNum, UINT32 uCmd, UINT32 uArg0, UINT32 uArg1)
{
	INT32 retval;
	UINT32 uReg;
	
	UART_BUFFER_T *dev;
		
	if((nNum < UART0) || (nNum > UART4))
		return UART_ENODEV;	
	
	dev = (UART_BUFFER_T *) &UART_DEV[nNum];
		
	/* Check UART initial status */
	if(dev->bIsUARTInitial == FALSE)
	{	
		if((uCmd != UART_IOC_GETERRNO) && 
		   (uCmd != UART_IOC_GETUARTREGISTERVALUE))
			return UART_EIO;
	}		
		
	switch(uCmd)
	{
		case UART_IOC_SETTXMODE: 
			if(uArg0 == UARTINTMODE)
				dev->bIsUseUARTTxInt = TRUE;
			else if(uArg0 == UARTPOLLMODE)	
				dev->bIsUseUARTTxInt = FALSE;
			else 
			{
				dev->nErrno = UART_ERR_OPERATE_MODE_INVALID;
				return UART_EIO;	
			}		
				
			break;
		
		case UART_IOC_SETRXMODE:
			if(uArg0 == UARTINTMODE)
			{
				dev->bIsUseUARTRxInt = TRUE;
				_uartEnableInterrupt(nNum, IER_RDA);
			}	
			else if(uArg0 == UARTPOLLMODE)		
			{
				dev->bIsUseUARTRxInt = FALSE;
				_uartDisableInterrupt(nNum, IER_RDA);
			}
			else
			{
				dev->nErrno = UART_ERR_OPERATE_MODE_INVALID;
				return UART_EIO;	
			}	
				
			break;
			
		case UART_IOC_GETRECCHARINFO:  // ..... not test yet  
			memcpy((PVOID) uArg0, (PVOID) dev, sizeof(struct UART_BUFFER_STRUCT));
			break;	
			
		case UART_IOC_SETUARTPARAMETER:  // ..... not test yet  
			if((retval = _uartConfigureUART((PVOID) uArg0)) < 0)
			{
				dev->nErrno = retval;
				return UART_EIO;	
			}	

			break;	

		case UART_IOC_PERFORMIrDA: 
			if (nNum != UART2)
				return UART_EIO;
		
			if((retval = _uartPerformIrDA(uArg0, uArg1)) < 0)
			{
				dev->nErrno = retval;
				return UART_EIO;	
			}	

			break;	

		case UART_IOC_GETUARTREGISTERVALUE: 
			return (_uartGetRegisterValue(nNum, (PVOID) uArg0));
			break;	
			
		case UART_IOC_GETERRNO:
			*(PUINT32)uArg0 = dev->nErrno;
			break;	
			
		case UART_IOC_SETMODEMLOOPBACK: 
			if( (nNum != UART3) && (nNum != UART1) )
				return UART_EIO;
				
			if(nNum == UART3)
			{	
				if(uArg0 == UART_ENABLE_LOOPBACK)  		 /* enable MODEM internal loop-back mode */
					outpb(REG_UART3_MCR, inpb(REG_UART3_MCR) | (1 << 4));  
				else if(uArg0 == UART_DISABLE_LOOPBACK)	 /* disable MODEM internal loop-back mode */
					outpb(REG_UART3_MCR, inpb(REG_UART3_MCR) & ~(1 << 4));   
				else
					return UART_EIO;	
			}
			else  /* UART1 */
			{	
				if(uArg0 == UART_ENABLE_LOOPBACK)  		 /* enable MODEM internal loop-back mode */
					outpb(REG_UART1_MCR, inpb(REG_UART1_MCR) | (1 << 4)); 
				else if(uArg0 == UART_DISABLE_LOOPBACK)	 /* disable MODEM internal loop-back mode */
					outpb(REG_UART1_MCR, inpb(REG_UART1_MCR) & ~(1 << 4)); 	
				else
					return UART_EIO;		
			}			
			break;
			
		case UART_IOC_SETMODEMINTERRUPT:
			if( (nNum != UART3) && (nNum != UART1) )
				return UART_EIO;
				
			if(nNum == UART3)
			{	
				if(uArg0 == UART_ENABLE_MODEM_INT)  
					_uartEnableInterrupt(UART3, IER_MOS);
				else if(uArg0 == UART_DISABLE_MODEM_INT)
					_uartDisableInterrupt(UART3, IER_MOS);
				else
					return UART_EIO;
			}
			else  /* UART1 */
			{
				if(uArg0 == UART_ENABLE_MODEM_INT)  
					_uartEnableInterrupt(UART1, IER_MOS);
				else if(uArg0 == UART_DISABLE_MODEM_INT)
					_uartDisableInterrupt(UART1, IER_MOS);
				else
					return UART_EIO;
			}				
			break;

		case UART_IOC_GETCTSSTATE: 
			if(nNum != UART1)        /* Only for UART channel 1 */    
				return UART_EIO;
				
			*(PUINT32)uArg0 = _uart_cCTSState;                        /* CTS state */	
			*(PUINT32)uArg1 = (inpb(REG_UART1_MSR) & (1 << 4)) >> 4;  /* get CTS# value */
			_uart_cCTSState = 0;
			break;		
			
		case UART_IOC_SETRTSSIGNAL:
			if(nNum != UART1)  /* Only for UART channel 1 */     
				return UART_EIO;
		
			if(uArg0 == UART_RTS_HIGH)      /* set RTS signal high */
				outpb(REG_UART1_MCR, inpb(REG_UART1_MCR) & ~0x02);
			else if(uArg0 == UART_RTS_LOW)  /* set RTS signal low  */
				outpb(REG_UART1_MCR, inpb(REG_UART1_MCR) | 0x02);
			else
				return UART_EIO;
				
			break;	
			
		case UART_IOC_GETDSRSTATE: 
			if ( (nNum != UART3) && (nNum != UART1) )  /* Only for UART channel 1 & 3 */
				return UART_EIO;
			
			if (nNum == UART3)
			{	
				*(PUINT32)uArg0 = _uart_cDSRState3;                       /* DSR state      */	
				*(PUINT32)uArg1 = (inpb(REG_UART3_MSR) & (1 << 5)) >> 5;  /* get DSR# value */
				_uart_cDSRState3 = 0;
			}
			else
			{
				*(PUINT32)uArg0 = _uart_cDSRState1;                       /* DSR state      */	
				*(PUINT32)uArg1 = (inpb(REG_UART1_MSR) & (1 << 5)) >> 5;  /* get DSR# value */
				_uart_cDSRState1 = 0;
			}	
			break;		
			
		case UART_IOC_SETDTRSIGNAL: 
			if ( (nNum != UART3) && (nNum != UART1) )  /* Only for UART channel 1 & 3 */
				return UART_EIO;
		
			if (nNum == UART3)
			{
				if(uArg0 == UART_DTR_HIGH)      /* set DTR signal high */
					outpb(REG_UART3_MCR, inpb(REG_UART3_MCR) & ~0x01);
				else if(uArg0 == UART_DTR_LOW)  /* set DTR signal low  */
					outpb(REG_UART3_MCR, inpb(REG_UART3_MCR) | 0x01);
				else
					return UART_EIO;
			}
			else
			{
				if(uArg0 == UART_DTR_HIGH)      /* set DTR signal high */
					outpb(REG_UART1_MCR, inpb(REG_UART1_MCR) & ~0x01);
				else if(uArg0 == UART_DTR_LOW)  /* set DTR signal low  */
					outpb(REG_UART1_MCR, inpb(REG_UART1_MCR) | 0x01);
				else
					return UART_EIO;
			}				
			break;
			
		case UART_IOC_SETINTERRUPT:  
			if(uArg0 == 1)       /* enable interrupt  */
				_uartEnableInterrupt(nNum, uArg1);
			else if(uArg0 == 0)  /* disable interrupt */
				_uartDisableInterrupt(nNum, uArg1);
			else
				return UART_EIO;
		
			break;	
			
		case UART_IOC_SETBREAKCONTROL: 
			uReg = inpb(REG_UART0_LCR + (nNum * UARTOFFSET));
			if(uArg0 == 1)       /* set break contorl bit  */
			{
				uReg |= 0x40;
				outpb(REG_UART0_LCR + (nNum * UARTOFFSET), uReg);
			}	
			else if(uArg0 == 0)  /* clear break contorl bit */
			{
				uReg &= ~0x40;
				outpb(REG_UART0_LCR + (nNum * UARTOFFSET), uReg);
			}	
			else
				return UART_EIO;
		
			break;	
			
		case UART_IOC_GETBIISTATE:  
			switch(nNum)
			{
				case UART0:
					*(PUINT32)uArg0 = _uart_cBIIState_0;
					break;					
				case UART1:
					*(PUINT32)uArg0 = _uart_cBIIState_1;
					break;					
				case UART2:
					*(PUINT32)uArg0 = _uart_cBIIState_2;
					break;					
				case UART3:
					*(PUINT32)uArg0 = _uart_cBIIState_3;
					break;			
				case UART4:
					*(PUINT32)uArg0 = _uart_cBIIState_4;
					break;			
			
				default: 
					break;	
			}
			break;

		/* H/W S/W flow control function */
		case UART_IOC_ENABLEHWFLOWCONTROL:    
			if(nNum != UART1)  // Only for UART 1
				return UART_EIO;	
						
			/* H/W & S/W are alterntive */				
			if(_uart_cFlowControlMode == SWFLOWCONTROL)  
				return UART_EIO;
		
			_uart_cFlowControlMode = HWFLOWCONTROL;
			
			/* Implement H/W flow control on TX & RX interrupt mode. */
			dev->bIsUseUARTTxInt = TRUE;
			dev->bIsUseUARTRxInt = TRUE;
			_uartEnableInterrupt(nNum, IER_RDA);
			
			/* 
				Set up RTS mechanism. 
				In uartReceiveChars(), if uRecCnt >= _uart_nMaxRxBuf then set RTS high to stop RX. 
				In uartReadRxBuf(), if uRecCnt <= _uart_nMinRxBuf then set RTS low to re-start RX. 
			*/
			_uart_nMaxRxBuf = (UARTRXBUFSIZE[UART1] * 3) / 4;  
			_uart_nMinRxBuf = UARTRXBUFSIZE[UART1] / 2;  	  
			FDEBUG("max[%d] min[%d]\n", _uart_nMaxRxBuf, _uart_nMinRxBuf);
		
			/* Set RTS as logic 1, RX stopped */
			outpb(REG_UART1_MCR, inpb(REG_UART1_MCR) & ~0x02);  /* set RTS signal high */
			
			/* Enable MODEM status interrupt */
			_uartEnableInterrupt(nNum, IER_MOS);
			
			/* 
				Maintain H/W flow control flag by read Modem Status Register.
				If CTS high, stop TX. 
				If CTS low, start TX.  
			*/
			if( inpw(REG_UART1_MSR) & 0x10 )  /* CTS external signal is low  */
				_uart_cHWTXStopped = 0;       /* TX started                  */
			else                              /* CTS external signal is high */
				_uart_cHWTXStopped = 1;       /* TX stopoed                  */ 
				
			/* Set RTS as logic 0, RX re-start */
			outpb(REG_UART1_MCR, inpb(REG_UART1_MCR) | 0x02);  /* set RTS signal low  */	
			_uart_cHWRXStopped = 0;  // RX started			
			break;
			
		case UART_IOC_DISABLEHWFLOWCONTROL:  
			if(nNum != UART1)  // Only for UART 1
				return UART_EIO;
		
			/* Disable MODEM status interrupt */
			_uartDisableInterrupt(nNum, IER_MOS);
			_uart_cFlowControlMode = 0;
			_uart_cHWTXStopped = 0;
			_uart_cHWRXStopped = 0;
			break;
			
		case UART_IOC_ENABLESWFLOWCONTROL:  /* Important: RX trigger levle should one byte */
			if(nNum != UART1)  // Only for UART 1
				return UART_EIO;		
			if(_uart_cFlowControlMode == HWFLOWCONTROL)  // Should disable H/W flow control first
				return UART_EIO;
				
			_uart_cFlowControlMode = SWFLOWCONTROL;	
				
			/* Implement S/W flow control on TX & RX interrupt mode. */
			dev->bIsUseUARTTxInt = TRUE;
			dev->bIsUseUARTRxInt = TRUE;
			_uartEnableInterrupt(nNum, IER_RDA);	
			
			/* 
				Set up S/W flow control mechanism of RX. 
				In uartReceiveChars(), if uRecCnt >= _uart_nMaxRxBuf then send Xoff (0x13). 
				In uartReadRxBuf(), if uRecCnt <= _uart_nMinRxBuf send Xon (0x11). 
			*/
			_uart_nMaxRxBuf = (UARTRXBUFSIZE[UART1] * 3) / 4;  
			_uart_nMinRxBuf = UARTRXBUFSIZE[UART1] / 2;  	  
			FDEBUG("max[%d] min[%d]\n", _uart_nMaxRxBuf, _uart_nMinRxBuf);		
						
			_uart_cSWTXStopped = 0;	
			_uart_cSWRXStopped = 0;			
			break;
			
		case UART_IOC_DISABLESWFLOWCONTROL:  
			if(nNum != UART1)  // Only for UART 1
				return UART_EIO;
		
			_uart_cFlowControlMode = 0;	
			_uart_cSWTXStopped = 0;	
			_uart_cSWRXStopped = 0;		
			break;			
			
		case UART_IOC_SETUART1FULLMODEM: 
			if(nNum != UART1)  /* Only for UART 1 */
				return UART_EIO;
			
			/* 
				Configure GPIOE as full modem, CTS & RTS & DTR & DSR (RI & DCD).
				Note that UART2 & UART4 will change the GPIO settings as UART1 full modem pins. 
			*/	
			outpw(REG_MFSEL, inpw(REG_MFSEL) | (1 << 9) | (1 << 10) | (1 << 12) | (1 << 13));				
			break;
			
		case UART_IOC_SETUART1HIGHSPEED:
			if (nNum != UART1)  /* Only for UART 1 */
				return UART_EIO;
				
			if ( (uArg0 != UART1_PLL0) &&
				 (uArg0 != UART1_PLL1) &&
				 (uArg0 != UART1_EXTAL15M) )
				return UART_EIO;	
			
			//uReg = inpw(REG_CLKSEL) & ~(0x300);
			//outpw(REG_CLKSEL, uReg | uArg0);
			outpw(REG_CLKSEL, ( inpw(REG_CLKSEL) & ~(0x300) ) | uArg0);
			outpw(REG_CLKDIV, inpw(REG_CLKDIV) & ~(0xF0000));  /* clear UART1DIV within CLKDIV */			
			
#ifdef UART1_DEBUG			
			_uartEnableInterrupt(UART1, IER_RLS);	
#endif			
			break;
			
		case UART_IOC_FLUSH_TX_BUFFER:
			dev->uUartTxTail = 0;
			dev->uUartTxHead = 0;			
			break;		
			
		case UART_IOC_FLUSH_RX_BUFFER:
			dev->uUartRxTail = 0;
			dev->uUartRxHead = 0;
			dev->uRecCnt = 0;			
			break;	
					
		default:
			return UART_ENOTTY;	
	}		
	
	return 0;
}	

/*-----------------------------------------------------------------------------------*/
/* Function:                                                                         */
/*   uartRelease                                                                     */
/*                                                                                   */
/* Parameters:                                                                       */
/*   nNum			 UART channel.                                                   */
/*                                                                                   */                                                                           
/* Returns:                                                                          */
/*   0				 Success.                                                        */  
/*   UART_EIO        No activated.                                                   */
/*   UART_ENODEV	 UART channel out of range.                                      */
/*                                                                                   */
/* Side effects:                                                                     */
/*   None.                                                                           */
/*                                                                                   */
/* Description:                                                                      */
/*   Release memory resource, disable interrupt.                                     */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/

INT32 uartRelease(INT nNum)
{
	UART_BUFFER_T *dev;
	
	if((nNum < UART0) || (nNum > UART4))
		return UART_ENODEV;	
	
	dev = (UART_BUFFER_T *) &UART_DEV[nNum];
	
	/* Check UART initial status */
	if(dev->bIsUARTInitial == FALSE)
		return UART_EIO;
		
	/* Disable all interrupt of the specific UART */
	_uartDisableInterrupt(nNum, DISABLEALLIER);
		
	/* Free memory */
	free(dev->pucUartTxBuf);
	free(dev->pucUartRxBuf);
	free(dev->pucUARTFlag);
	
	/* Initial parameter */
	dev->bIsUARTInitial = FALSE;  /* it's important */

	return 0;
}		
